Micron Document
Gopher Proxy


akewebdump.ddns.net:70 akewebdump.ddns.net:70/0/dump/omsk-curses
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#!/usr/bin/python3
import enum, socket, traceback, subprocess, curses
from curses.textpad import Textbox, rectangle

class MenuType(str, enum.Enum):
DOC = '0'
MENU = '1'
QUERY = '7'
BIN = '9'
HTML = 'h'
INFO = 'i'
SOUND = 's'
IMAGE = 'I'
GIF = 'g'

ESC_WHITE = '\033[1;37m'
ESC_CYAN = '\033[1;36m'
ESC_RESET = '\033[0m'
ESC_RED = '\033[1;31m'
ESC_GREEN = '\033[1;32m'
ESC_YELLO = '\033[1;33m'
ESC_BLUE = '\033[1;34m'

class Fetcher(object):
def __init__(self):
self.mode = MenuType.MENU
self.host = 'ake.crabdance.com'
self.port = 70
self.path = ''

self.history = []

def go(self, mode=None, host=None, port=None, path=None, query=None):
self.history.append((
self.mode, self.host, self.port, self.path
))
if mode is not None:
self.mode = mode
if host is not None:
self.host = host
if port is not None:
self.port = int(port)
if path is not None:
self.path = path
if mode == MenuType.QUERY:
self.path += '\t{}'.format(query)

def back(self):
try:
mode, host, port, path = self.history.pop()
self.mode = mode
self.host = host
self.port = port
self.path = path
except:
print('No entries in history')

def fetch(self):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.host, self.port))
s.sendall('{}\r\n'.format(self.path).encode('utf-8'))
resp = b''
while True:
chunk = s.recv(1024)
if not chunk:
break
resp += chunk
return resp
except:
traceback_exc()
print('Failed to fetch data')
return None

class Navigator(Fetcher):
def __init__(self):
super().__init__()
self.links = []
self.buffer = []
def goto(self, num):
if num <= 0 or num > len(self.links):
print('\tWrong link number')
else:
self.go(*self.links[num - 1])
def render(self):
self.buffer = []
if self.mode == MenuType.BIN:
print('Enter filename or leave empty to ignore')
filename = input('Save as: ')
if filename == '':
return
elif self.mode == MenuType.HTML:
if self.path.lower().startswith('url:'):
subprocess.call(['/usr/bin/xdg-open', self.path[4:]])
else:
subprocess.call(['/usr/bin/lynx', 'gopher://{}:{}/0{}'.format(self.host, self.port, self.path)])
self.back()
return
data = self.fetch()
if self.mode == MenuType.DOC:
self.buffer = data.decode('utf-8').split('\n')
elif self.mode == MenuType.BIN:
with open(filename, 'wb') as f:
f.write(data)
elif self.mode == MenuType.MENU or self.mode == MenuType.QUERY:
self.links = []
items = [item.split('\t') for item in data.decode('utf-8').split('\n')]
for item in items:
if len(item) < 4:
continue
itype = item[0][0]
itext = item[0][1:]
if itype == MenuType.INFO:
self.buffer.append((None, itext))
elif itype == MenuType.DOC:
self.links.append((itype, item[2], item[3], item[1]))
self.buffer.append((
len(self.links),
itext
))
elif itype == MenuType.MENU:
self.links.append((itype, item[2], item[3], item[1]))
self.buffer.append((
len(self.links),
itext
))
elif itype == MenuType.BIN or itype == MenuType.SOUND or itype == MenuType.IMAGE or itype == MenuType.GIF:
self.links.append((itype, item[2], item[3], item[1]))
self.buffer.append((
len(self.links),
itext
))
elif itype == MenuType.QUERY:
self.links.append((itype, item[2], item[3], item[1]))
self.buffer.append((
len(self.links),
itext
))
elif itype == MenuType.HTML:
self.links.append((itype, item[2], item[3], item[1]))
self.buffer.append((
len(self.links),
itext
))
else:
self.buffer.append((None, 'Unknown type: {}'.format(itype)))
else:
self.buffer.append('\tNot yet implemented')

def display_help():
print('''Omsk subway -- commandline gopher navigator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Commands:

exit -- exit program
help -- view this help
back -- return to previous menu
index -- return to server index
@<host> -- request server's index
<selector> -- request menu by selector on current
server
''')

def main(stdscr):
navi.render()
start = 0
link = 0
key = None
history = []
while True:
stdscr.clear()
stdscr.addstr(0, 0, '[ gopher://{}:{}/{}{} ]'.format(navi.host, navi.port, navi.mode, navi.path), curses.A_BOLD)
stdscr.refresh()
first = None
last = None
for i in range(min(start + curses.LINES - 2, len(navi.buffer) - start)):
color = curses.A_NORMAL
if navi.mode == MenuType.MENU:
if navi.buffer[i + start][0] == None:
text = ' ' + navi.buffer[i + start][1]
else:
color = curses.A_BOLD
if first is None:
first = navi.buffer[i + start][0] - 1
last = navi.buffer[i + start][0] - 1
if navi.buffer[i + start][0] == link + 1:
text = '> ' + navi.buffer[i + start][1]
else:
text = ' ' + navi.buffer[i + start][1]
else:
text = navi.buffer[i + start]
try:
stdscr.addstr(i + 1, 0, text, color)
except:
pass
stdscr.move(curses.LINES - 1, 0)
stdscr.refresh()
key = stdscr.getkey()
if key == 'KEY_PPAGE':
start = max(0, start - curses.LINES - 2)
elif key == 'KEY_NPAGE':
start = start + curses.LINES - 2
elif key == 'KEY_RIGHT' or key == '\n':
if navi.links[link][0] == MenuType.QUERY:
curses.echo()
stdscr.addstr(curses.LINES - 1, 0, 'Query: ')
s = stdscr.getstr(curses.LINES - 1, 7).decode('utf-8')
curses.noecho()
navi.go(*navi.links[link], query=s)
else:
navi.goto(link + 1)
history.append((start, link))
link = 0
start = 0
navi.render()
elif key == 'KEY_LEFT':
navi.back()
if len(history) > 0:
start, link = history.pop()
navi.render()
elif key == 'KEY_UP':
if navi.mode == MenuType.DOC:
continue
link = (link - 1) % len(navi.links)
if link < first:
start = max(0, start - curses.LINES - 2)
elif key == 'KEY_DOWN':
if navi.mode == MenuType.DOC:
continue
link = (link + 1) % len(navi.links)
if link > last:
start += curses.LINES - 2
elif link == 0:
start = 0
elif key == 'g':
curses.echo()
stdscr.addstr(curses.LINES - 1, 0, 'Host: ')
s = stdscr.getstr(curses.LINES - 1, 6).decode('utf-8')
curses.noecho()
parts = s.split(':')
navi.go(host=parts[0], port=70 if len(parts) < 2 else int(parts[1]), path='', mode=MenuType.MENU)
history.append((start, link))
link = 0
start = 0
navi.render()
elif key == 'q':
break
elif str(key).isdigit():
stdscr.addstr(curses.LINES - 1, 0, key)
curses.echo()
s = str(key) + stdscr.getstr(curses.LINES - 1, 1)
navi.goto()
curses.noecho()

if __name__ == '__main__':
navi = Navigator()
curses.wrapper(main)
you're on akewebdump.ddns.net^70/0/dump/omsk-curses
back link is ake.crabdance.com:70/1/dump